tdf#112975 WIN correctly handle VclInputFlags::OTHER

On Windows we can just check the message queue for existing
messages. But VclInputFlags::OTHER is used to check for any
messages, which can't be explicitly checked.

In the case of checking for VclInputFlags::OTHER while
excluding an other message type, we have to make multiple
PeekMessage calls and exclude all non-checked message ids.

Change-Id: I1cedd4b76444769842c74228fc547f0d924f8b60
Reviewed-on: https://gerrit.libreoffice.org/43337
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
diff --git a/vcl/inc/win/saltimer.h b/vcl/inc/win/saltimer.h
index 37976bb..68973e1 100644
--- a/vcl/inc/win/saltimer.h
+++ b/vcl/inc/win/saltimer.h
@@ -47,6 +47,7 @@
    virtual void Stop() override;

    inline bool IsDirectTimeout() const;
    inline bool HasTimerElapsed() const;
};

inline bool WinSalTimer::IsDirectTimeout() const
@@ -54,6 +55,11 @@
    return m_bDirectTimeout;
}

inline bool WinSalTimer::HasTimerElapsed() const
{
    return m_bDirectTimeout || ExistsValidEvent();
}

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/app/salinst.cxx b/vcl/win/app/salinst.cxx
index 38db3d3..ade3322 100644
--- a/vcl/win/app/salinst.cxx
+++ b/vcl/win/app/salinst.cxx
@@ -689,10 +689,119 @@
    return nRet;
}

struct MsgRange
{
    UINT nStart;
    UINT nEnd;
};

static std::vector<MsgRange> GetOtherRanges( VclInputFlags nType )
{
    assert( nType != VCL_INPUT_ANY );

    // this array must be kept sorted!
    const UINT nExcludeMsgIds[] =
    {
        0,

        WM_MOVE, // 3
        WM_SIZE, // 5
        WM_PAINT, // 15
        WM_KEYDOWN, // 256
        WM_TIMER, // 275

        WM_MOUSEFIRST, // 512
        513,
        514,
        515,
        516,
        517,
        518,
        519,
        520,
        WM_MOUSELAST, // 521

        SAL_MSG_POSTMOVE, // WM_USER+136
        SAL_MSG_POSTCALLSIZE, // WM_USER+137

        SAL_MSG_TIMER_CALLBACK, // WM_USER+162

        UINT_MAX
    };
    const unsigned MAX_EXCL = SAL_N_ELEMENTS( nExcludeMsgIds );

    bool aExcludeMsgList[ MAX_EXCL ] = { false, };
    std::vector<MsgRange> aResult;

    // set the excluded values
    if ( !(nType & VclInputFlags::MOUSE) )
    {
        for ( unsigned i = 0; nExcludeMsgIds[ 6 + i ] <= WM_MOUSELAST; ++i )
            aExcludeMsgList[ 6 + i ] = true;
    }

    if ( !(nType & VclInputFlags::KEYBOARD) )
        aExcludeMsgList[ 4 ] = true;

    if ( !(nType & VclInputFlags::PAINT) )
    {
        aExcludeMsgList[ 1 ] = true;
        aExcludeMsgList[ 2 ] = true;
        aExcludeMsgList[ 3 ] = true;
        aExcludeMsgList[ 16 ] = true;
        aExcludeMsgList[ 17 ] = true;
    }

    if ( !(nType & VclInputFlags::TIMER) )
    {
        aExcludeMsgList[ 5 ]  = true;
        aExcludeMsgList[ 18 ]  = true;
    }

    // build the message ranges to check
    MsgRange aRange = { 0, 0 };
    bool doEnd = true;
    for ( unsigned i = 1; i < MAX_EXCL; ++i )
    {
        if ( aExcludeMsgList[ i ] )
        {
            if ( !doEnd )
            {
                if ( nExcludeMsgIds[ i ] == aRange.nStart )
                    ++aRange.nStart;
                else
                    doEnd = true;
            }
            if ( doEnd )
            {
                aRange.nEnd = nExcludeMsgIds[ i ] - 1;
                aResult.push_back( aRange );
                doEnd = false;
                aRange.nStart = aRange.nEnd + 2;
            }
        }
    }

    if ( aRange.nStart != UINT_MAX )
    {
        aRange.nEnd = UINT_MAX;
        aResult.push_back( aRange );
    }

    return aResult;
}

bool WinSalInstance::AnyInput( VclInputFlags nType )
{
    MSG aMsg;

    if ( nType & VclInputFlags::TIMER )
    {
        const WinSalTimer* pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
        if ( pTimer && pTimer->HasTimerElapsed() )
            return true;
    }

    if ( (nType & VCL_INPUT_ANY) == VCL_INPUT_ANY )
    {
        // revert bugfix for #108919# which never reported timeouts when called from the timer handler
@@ -702,32 +811,52 @@
    }
    else
    {
        const bool bCheck_KEYBOARD (nType & VclInputFlags::KEYBOARD);
        const bool bCheck_OTHER    (nType & VclInputFlags::OTHER);

        // If there is a modifier key event, it counts as OTHER
        // Previously we were simply ignoring these events...
        if ( bCheck_KEYBOARD || bCheck_OTHER )
        {
            if ( PeekMessageW( &aMsg, nullptr, WM_KEYDOWN, WM_KEYDOWN,
                                  PM_NOREMOVE | PM_NOYIELD ) )
            {
                const bool bIsModifier = ( (aMsg.wParam == VK_SHIFT) ||
                    (aMsg.wParam == VK_CONTROL) || (aMsg.wParam == VK_MENU) );
                if ( bCheck_KEYBOARD && !bIsModifier )
                    return true;
                if ( bCheck_OTHER && bIsModifier )
                    return true;
            }
        }

        // Other checks for all messages not excluded.
        // The less we exclude, the less ranges have to be checked!
        if ( bCheck_OTHER )
        {
            // TIMER and KEYBOARD are already handled, so always exclude them!
            VclInputFlags nOtherType = nType &
                ~VclInputFlags(VclInputFlags::KEYBOARD | VclInputFlags::TIMER);

            std::vector<MsgRange> aMsgRangeList( GetOtherRanges( nOtherType ) );
            for ( MsgRange aRange : aMsgRangeList )
                if ( PeekMessageW( &aMsg, nullptr, aRange.nStart,
                                   aRange.nEnd, PM_NOREMOVE | PM_NOYIELD ) )
                    return true;

            // MOUSE and PAINT already handled, so skip futher checks
            return false;
        }

        if ( nType & VclInputFlags::MOUSE )
        {
            // Test for mouse input
            if ( PeekMessageW( &aMsg, nullptr, WM_MOUSEFIRST, WM_MOUSELAST,
                                  PM_NOREMOVE | PM_NOYIELD ) )
                return true;
        }

        if ( nType & VclInputFlags::KEYBOARD )
        {
            // Test for key input
            if ( PeekMessageW( &aMsg, nullptr, WM_KEYDOWN, WM_KEYDOWN,
                                  PM_NOREMOVE | PM_NOYIELD ) )
            {
                if ( (aMsg.wParam == VK_SHIFT)   ||
                     (aMsg.wParam == VK_CONTROL) ||
                     (aMsg.wParam == VK_MENU) )
                    return false;
                else
                    return true;
            }
        }

        if ( nType & VclInputFlags::PAINT )
        {
            // Test for paint input
            if ( PeekMessageW( &aMsg, nullptr, WM_PAINT, WM_PAINT,
                                  PM_NOREMOVE | PM_NOYIELD ) )
                return true;
@@ -748,22 +877,6 @@
                                  PM_NOREMOVE | PM_NOYIELD ) )
                return true;
        }

        if ( nType & VclInputFlags::TIMER )
        {
            // Test for timer input
            if ( PeekMessageW( &aMsg, nullptr, WM_TIMER, WM_TIMER,
                                  PM_NOREMOVE | PM_NOYIELD ) )
                return true;

        }

        if ( nType & VclInputFlags::OTHER )
        {
            // Test for any input
            if ( PeekMessageW( &aMsg, nullptr, 0, 0, PM_NOREMOVE | PM_NOYIELD ) )
                return true;
        }
    }

    return false;